package com.ls.blog.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.google.gson.JsonObject;
import com.ls.blog.common.consts.ChinaCitys;
import com.ls.blog.common.exception.BizException;
import com.ls.blog.dto.*;
import com.ls.blog.entity.Article;
import com.ls.blog.entity.User;
import com.ls.blog.entity.UserRole;
import com.ls.blog.mapper.UserMapper;
import com.ls.blog.producer.EmailProducer;
import com.ls.blog.service.*;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.ls.blog.utils.*;
import com.ls.blog.vo.PageVO;
import com.ls.blog.vo.UserVO;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.client.RestTemplate;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import javax.validation.constraints.Email;
import javax.validation.constraints.NotBlank;
import java.io.UnsupportedEncodingException;
import java.time.LocalDateTime;
import java.util.*;
import java.util.stream.Collectors;

import static com.ls.blog.common.consts.RedisKeyConst.*;

/**
 * <p>
 *  服务实现类
 * </p>
 *
 * @author lishun
 * @since 2022-01-14
 */
@Service
public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements UserService {

    @Resource
    private UserMapper userMapper;

    @Autowired
    private UserRoleService userRoleService;

    @Autowired
    private ArticleService articleService;

    @Autowired
    private TagService tagService;

    @Autowired
    private CategoryService categoryService;

    @Autowired
    private RedisUtil redisUtil;

    @Autowired
    private HttpServletRequest httpServletRequest;

    @Autowired
    private MailUtils mailUtils;

    @Autowired
    private EmailProducer producer;

    @Autowired
    private BCryptPasswordEncoder encoder;


    @Autowired
    private RestTemplate restTemplate;

    static final String CheckCode= "CheckCode";



    @Override
    public UserDetailsIml getUserDTOByUserName(String userName) {
        User user = this.getOne(new LambdaQueryWrapper<User>().eq(User::getUsername, userName));
        return ObjectUtils.copyObject(user, UserDetailsIml.class);
    }

    @Override
    public Recodes<UserDTO> userDTOList(PageVO pageVO) {
        Recodes<UserDTO> recodes = new Recodes<>();
        long count = this.count();
        List<UserDTO> userDTOList = userMapper.userDTOList(PageUtils.translate(pageVO));
        recodes.setTotal(count);
        recodes.setRecodes(userDTOList);
        return recodes;
    }

    @Override
    @Transactional
    public void editRole(UserVO userVO) {
        // 删除原来用户角色中间表
        userRoleService.remove(new LambdaQueryWrapper<UserRole>().eq(UserRole::getUid,userVO.getId()));
        List<Integer> roleIds = userVO.getRoleIds();
        List<UserRole> userRoleList = roleIds.stream().map(item -> {
            UserRole userRole = new UserRole();
            userRole.setRid(item);
            userRole.setUid(userVO.getId());
            return userRole;
        }).collect(Collectors.toList());
        userRoleService.saveBatch(userRoleList);
    }

    @Override
    public AboutDTO about() {

        long articleCount = articleService.count(new LambdaQueryWrapper<Article>()
                .eq(Article::getIsDelete, 0)
                .eq(Article::getShareStatement, 1));
        // 标签数量
        long tagCount = tagService.count();
        // 分类数量
        long categoryCount = categoryService.count();

        return AboutDTO.builder()
                .name("lishun")
                .address("重庆")
                .avatar("https://lishungulimall.oss-cn-shenzhen.aliyuncs.com/Wed%20Feb%2009%2007:56:09%20UTC%202022/5db3fea5ba5545d0bb4b280ba542ee55src=http___up.enterdesk.com_edpic_ac_0e_f3_ac0ef38626238b85339238217ef8842e.jpg&refer=http___up.enterdesk.jpg")
                .articleCount(articleCount)
                .tagCount(tagCount)
                .categoryCount(categoryCount)
                .build();
    }

    @Override
    public String getValidCode(@Validated UserVO userVO) {
        // jsr303校验过了
        StringBuilder str = new StringBuilder();
        Random random = new Random();
        for (int i = 0; i < 6; i++) {
            str.append(random.nextInt(10));
        }
        redisUtil.hset(CheckCode,userVO.getEmail(),str.toString());
        redisUtil.expire(CheckCode,1000*50*5);
        EmailDTO emailDTO = new EmailDTO();
        emailDTO.setBcc();
        emailDTO.setSubject("用户注册");
        emailDTO.setTo(userVO.getEmail());
        emailDTO.setText("你的验证码是"+str.toString()+"，五分钟有效");
        producer.sendMail(emailDTO);
        return str.toString();
    }

    @Override
    public void register(UserVO userVO) {
        // 校验验证码
        String checkCode = userVO.getCheckCode();
        String email = userVO.getEmail();
        Object obj = redisUtil.hget(CheckCode, email);
        if (obj==null || !((String) obj).equals(checkCode)){
            throw new BizException("验证码错误");
        }
        // 用完删除
        redisUtil.hdel(CheckCode,email);
        // 查询是否已经注册
        long count = this.count(new LambdaQueryWrapper<User>().eq(User::getUsername, userVO.getEmail()));
        if (count>0){
            throw new BizException("用户已经注册");
        }
        // 密码不能存明文到数据库，要加密
        User user = User.builder()
                .username(email)
                .password(encoder.encode(userVO.getPassword()))
                .nickname(userVO.getNickname())
                .build();
        this.save(user);
    }


    @Override
    public String pub() {
        // 查询redis
        // 没有则返回默认值
        Object o = redisUtil.get(PUB);
        if (Objects.isNull(o)){
            return "请到网站后台设置";
        }
        return o.toString();
    }

    @Override
    public void setPub(String content) throws UnsupportedEncodingException {
        redisUtil.set(PUB,content);
    }

    @Override
    public SiteDTO siteInfo() {
        // 先添加UV，再返回今日和昨日UV
        String key = UV+DateUtils.getDateByString();
        redisUtil.hllAdd(key,IpUtil.getIpAddr(httpServletRequest));

        // 获得今日UV
        long today = redisUtil.hllGet(key);
        // 获得昨日UV
        long yesterDay = redisUtil.hllGet(UV + DateUtils.getBeforeDay());
        // 获得本月IP
        // 1构建本月的keys
        // 当前第几天就构造几个key
        String[] keys = new String[DateUtils.getCurrentDay()];
        for (int i = 0; i < keys.length; i++) {
            if (i<9){
                keys[i] = UV+DateUtils.getYearAndMonth()+"0"+(i+1);
            }else {
                keys[i] = UV+DateUtils.getYearAndMonth()+(i+1);
            }
        }
        long month = redisUtil.hllGet(keys);

        return SiteDTO.builder()
                .toDay(today)
                .yesterday(yesterDay)
                .month(month)
                .build();
    }

    public static void main(String[] args) {
        String[] keys = new String[DateUtils.getCurrentDay()];
        for (int i = 0; i < keys.length; i++) {
            keys[i] = UV+DateUtils.getYearAndMonth()+(i+1);
            System.out.println(keys[i]);
        }
    }


    @Override
    public Map<String, Integer> userMap() {
        Map<String, Integer> map = new HashMap<>();
        Object o = redisUtil.get(MAP);
        if (Objects.nonNull(o)){
            map= (Map<String, Integer>) o;
            return map;
        }
        String[] city =  ChinaCitys.citys;
        List<User> list = this.list(new LambdaQueryWrapper<User>().select(User::getIp).isNotNull(User::getIp));
        for (User user : list) {
            if (user.getIp()==null){
                continue;
            }
            String ipSource = IpUtil.getIpSource(user.getIp());
            // 二层遍历
            for (String s : city) {
                if (Objects.nonNull(ipSource) && ipSource.startsWith(s)){
                  map.put(s,Objects.nonNull(map.get(s))?map.get(s)+1:1);
                }
            }
        }
        redisUtil.set(MAP,map,1000*60*24);
        return map;
    }

    @Override
    public Object music() {
        JsonObject object = restTemplate.getForObject("https://c.y.qq.com/v8/fcg-bin/fcg_v8_toplist_cp.fcg?g_tk=5381&uin=0&format=json&inCharset=utf-8&outCharset=utf-8&notice=0&platform=h5&needNewCode=1&tpl=3&page=detail&type=top&topid=36&_=1520777874472", JsonObject.class);
        return object;
    }

    @Override
    public Object loadUserByPhoneNum(long parseLong) {
        return null;
    }
}
